package utils

import (
	"fmt"
	"github.com/gomodule/redigo/redis"
	"log"
	"time"
)

func (lock *RedigoLockLua) acquireLock() bool {
	var script string
	switch lock.Type {
	case MillisecondLockType:
		script = `
        if redis.call('exists', KEYS[1]) == 0 then
            redis.call('set', KEYS[1], ARGV[1], 'PX', ARGV[2])
            return true
        else
            return false
        end
    `
	default:
		script = `
        if redis.call('exists', KEYS[1]) == 0 then
            redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2])
            return true
        else
            return false
        end
    `
	}

	result, err := redis.String(lock.Conn.Do("EVAL", script, 1, lock.Key, lock.Value, lock.KeyExistenceTime))
	if err != nil {
		log.Fatalf("Error executing Lua script: %v", err)
	}

	return result == "true"
}

func (lock *RedigoLockLua) releaseLock() bool {
	script := `
    if redis.call("GET", KEYS[1]) == ARGV[1] then
        return redis.call("DEL", KEYS[1])
    else
        return 0
    end
    `

	result, err := redis.Int(lock.Conn.Do("EVAL", script, 1, lock.Key, lock.Value))
	if err != nil {
		log.Fatalf("Error executing Lua script: %v", err)
	}

	return result == 1
}

type LockType int32

const (
	MillisecondLockType LockType = 0
	SecondLockType      LockType = 1
)

type RedigoLockLua struct {
	Key                string     //key
	Value              string     //value
	Conn               redis.Conn //redis pool
	KeyExistenceTime   int        //nx key 存在时间
	MaxLockIndex       int        //最大尝试加锁次数
	EveryIndexInterval int        //每次尝试间隔时间
	Type               LockType   //锁类型 毫秒级/秒级
}

func (lock *RedigoLockLua) TryLock() (ok bool) {
	lockFlag := lock.acquireLock()
	if !lockFlag {
		index := 0
		// The lock was not successful, it already exists.
		for {
			if index >= lock.MaxLockIndex || lock.EveryIndexInterval == 0 {
				return false
			}
			lockFlag = lock.acquireLock()
			if lockFlag {
				return true
			}
			switch lock.Type {
			case MillisecondLockType:
				time.Sleep(time.Millisecond * time.Duration(lock.EveryIndexInterval))
			default:
				time.Sleep(time.Second * time.Duration(lock.EveryIndexInterval))
			}
			index++
		}
	} else {
		return true
	}
}

func (lock *RedigoLockLua) Unlock() {
	lock.releaseLock()
	defer lock.Conn.Close()
	return
}

func genNxKey(source string) string {
	return fmt.Sprintf("redislock:%s", source)
}

func NewRedigoLockLua(key, value string, keyExistenceTime, maxLockIndex, everyIndexInterval int, LockType LockType) *RedigoLockLua { //resource为key;token为value;keyExistenceTime为key存在时长时间单位s;maxLockIndex为最大尝试加锁次数,间隔时间300ms
	return &RedigoLockLua{
		Key:                genNxKey(key),
		Value:              value,
		Conn:               getRedisPool(1).Get(),
		KeyExistenceTime:   keyExistenceTime,
		MaxLockIndex:       maxLockIndex,
		EveryIndexInterval: everyIndexInterval,
		Type:               LockType,
	}
}
